#include "net/ssl_context.hh"

namespace wb
{
namespace net
{
uint64_t ssl_context::ssl_context_count_{0};
std::mutex ssl_context::ssl_context_mtx_{};

ssl_context::ssl_context()
{
    {
        std::lock_guard<std::mutex> g{ssl_context_mtx_};
        if (ssl_context_count_ == 0)
        {
            OPENSSL_init_ssl(0, nullptr);
        }
        ++ssl_context_count_;
    }

    handle_ = SSL_CTX_new(TLS_method());
    if (handle_ == nullptr)
    {
        throw std::runtime_error(
            "wb::net::ssl_context::SSL_CTX_new(TLS_method())");
    }

    SSL_CTX_set_options(handle_, SSL_OP_ALL | SSL_OP_NO_SSLv3);
}

ssl_context::ssl_context(const ssl_context_opts& op)
{
    int ret = SSL_CTX_use_certificate_file(handle_,
                                           op.certfile.c_str(),
                                           static_cast<int>(op.certfiletype));
    if (ret != 1)
    {
        throw std::runtime_error("unable to load certfile: " +
                                 op.certfile.string());
    }
    ret = SSL_CTX_use_PrivateKey_file(handle_,
                                      op.privkey.c_str(),
                                      static_cast<int>(op.privkeytype));
    if (ret != 1)
    {
        throw std::runtime_error("unable to load privkey: " +
                                 op.privkey.string());
    }
    ret = SSL_CTX_check_private_key(handle_);
    if (ret != 1)
    {
        throw std::runtime_error("Certificate and private key check failed");
    }
}

ssl_context::~ssl_context()
{
    if (handle_)
    {
        SSL_CTX_free(handle_);
        handle_ = nullptr;
    }
}
}  // namespace net
}  // namespace wb